package net.chrisrichardson.ec2deployer.core

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Properties;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import java.util.Calendar
import org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.methods.GetMethod;


class AwsCredentialsVerifier {

    AWSProperties awsProperties
    AwsCredentialVerificationResults results

    AwsCredentialsVerifier(AWSProperties awsProperties, AwsCredentialVerificationResults results) {
        this.awsProperties = awsProperties
        this.results = results
    }

    AwsCredentialsVerifier(AWSProperties awsProperties) {
        this(awsProperties, new AwsCredentialVerificationResultsToStdOut())
    }
   
    void verify() {
        verifyS3();
        verifyEc2();
        verifyKeyPair();
        verifyFirewallAllowsSsh();
        verifyKeyPairPermissions()
        verifySshCommandAvailable()
    }

    void verifyS3() {
        print "Accessing S3..."
        String url = makeSignedUrl("/", awsProperties.accessKey, awsProperties.secretKey)
        HttpClient client = new HttpClient()
        GetMethod gm = new GetMethod(url)
        int statusCode = client.executeMethod(gm)
        if (statusCode == 200)
            results.success("s3")
        else {
            results.fail("s3", statusCode.toString())
            println gm.getResponseBodyAsString()
        }
    }

    void verifyEc2() {
        print "Accessing EC2..."
        try {
            def requestor = new EC2RequestExecutor(awsProperties)
            def params = ['Action': 'DescribeImages']
            def p = requestor.executeRequest(params)
            println "SUCCESS"
            List availableAmis = p.imagesSet.item.collect {it.imageId[0].text()}

            def imageTypes = ["m1.small", "m1.large", "m1.xlarge"]
            for (imageType in imageTypes) {
                String propertyName = "imageId." + imageType
                if (awsProperties.exists(propertyName)) {
                    String amiId = awsProperties.getProperty(propertyName)
                    if (availableAmis.contains(amiId))
                        results.success("Access to the $imageType AMI")
                    else
                        results.fail("Access to the $imageType AMI", "No access")
                }
            }
        } catch (Exception e) {
            results.exception("ec2", e)
        }
    }

    void verifyKeyPair() {
        print "Verifying keypair..."
        try {
            def requestor = new EC2RequestExecutor(awsProperties)
            def params = ['Action': 'DescribeKeyPairs']
            def p = requestor.executeRequest(params)
            List keyPairNames = p.keySet.item.collect {it.keyName[0].text()}
            if (keyPairNames.contains(awsProperties.keyName))
                results.success("keypair exists");
            else
                results.fail("key pair exists", "Could not find keypair: " + awsProperties.keyName)
        } catch (Exception e) {
            results.exception("keypair", e)
        }
    }


    private final String HMAC_SHA1_ALGORITHM = "HmacSHA1";

    public String makeSignedUrl(String path, String accessKey, String secretKey) {
        String contentMd5 = "";
        String contentType = "";
        String expires = Math.round((oneHourFromNow().getTime() / 1000)) + "";
        String canonicalizedAmzHeaders = "";
        String canonicalizedResource = path;

        String stringToSign = "GET" + "\n" + contentMd5 + "\n" + contentType + "\n" + expires + "\n" + canonicalizedAmzHeaders + canonicalizedResource;

        String signature = sign(stringToSign, secretKey);
        String urlEncodedSignature;
        try {
            urlEncodedSignature = URLEncoder.encode(signature, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return "https://s3.amazonaws.com${path}?AWSAccessKeyId=${accessKey}&Expires=${expires}&Signature=${urlEncodedSignature}"
    }

    private String sign(String stringToSign, String secret) {
        try {
            SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes("UTF8"),
                    HMAC_SHA1_ALGORITHM);
            Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
            mac.init(signingKey);
            byte[] rawHmac = mac.doFinal(stringToSign.getBytes("UTF8"));
            return new String(Base64.encodeBase64(rawHmac));
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate HMAC : ", e);
        }
    }

    private Date oneHourFromNow() {
        Calendar c = Calendar.getInstance();
        c.add(Calendar.HOUR, 1);
        return c.getTime();
    }

    void verifyFirewallAllowsSsh() {
    }

    void verifyKeyPairPermissions() {
        String path = awsProperties.keyPairFile
        if (new File(path).exists()) {
            println "SUCCESS. keyPairFile exists"
            println "IMPORTANT: Make sure that it is chmod 0600"
        } else
            println "ERROR. keyPairFile($path) does not exist"
    }

    void verifySshCommandAvailable() {
        String path = awsProperties.sshDir + "/ssh"
        if (new File(path).exists() || new File(path + ".exe").exists())
            println "SUCCESS. Ssh command available"
        else
            println "ERROR. Ssh ($path) not available"
    }
}
